package com.pi.shisan.service.schedule.job;

import com.pi.shisan.component.impl.WeatherComponent;
import com.pi.shisan.service.audio.AudioEffectComponentsSupplier;
import com.pi.shisan.service.audio.AudioLevelManager;
import com.pi.shisan.service.audio.AudioPlayer;
import com.pi.shisan.service.audio.MultiFilePlayer;
import com.pi.shisan.service.audio.WavPlayer;
import com.pi.shisan.service.constants.DndModeTypeEnum;
import com.pi.shisan.service.dto.AudioInfoDTO;
import com.pi.shisan.service.dto.ClockInfoDTO;
import com.pi.shisan.service.dto.LedDndModeSettingDTO;
import com.pi.shisan.service.dto.RunnableClockInfo;
import com.pi.shisan.service.dto.WeatherInfoDTO;
import com.pi.shisan.service.schedule.LoadingComponent;
import com.pi.shisan.service.schedule.job.support.EmptyPlayer;
import com.pi.shisan.service.service.ClockInfoService;
import com.pi.shisan.service.service.LedDndModeSettingService;
import com.pi.shisan.service.service.VolumeSettingService;
import com.pi.shisan.service.service.WeatherService;
import com.pi.shisan.service.util.ComponentManager;
import com.pi.shisan.service.util.SingleThreadCreator;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;

@Component
@Slf4j
public class SecondNotifyJob implements Job {

    @Autowired
    private ClockInfoService clockInfoService;

    @Autowired
    private Consumer<Integer> playBeforeVolume;

    @Autowired
    private VolumeSettingService volumeSettingService;

    @Autowired
    private WeatherService weatherService;


    @Autowired
    private LedDndModeSettingService ledDndModeSettingService;

    private ComponentManager componentManager;

    /**
     * 音频优先级管理器
     * 1 - 整点报时
     * 2 - 闹铃
     * 3 - 用户自行播放音乐
     */
    private AudioLevelManager audioLevelManager;

    /**
     * 空播放器，用于模拟播放来展示时间
     */
    private EmptyPlayer timeShower;

    /**
     * led和整点报时免打扰模式开关
     */
    private LedDndModeSettingDTO ledDndMode;
    private LedDndModeSettingDTO clockDndMode;

    /**
     * 上次dndModeStatus
     * 0 - 未初始化
     * 1 - 安静模式
     * 2 - 正常模式
     */
    private int lastLedDndModeStatus;

    @Autowired
    public void setComponentManager(ComponentManager componentManager) {
        if (this.componentManager == null) {
            this.componentManager = componentManager;
        }
    }

    @Autowired
    public void setAudioLevelManager(AudioLevelManager audioLevelManager) {
        if (this.audioLevelManager == null) {
            this.audioLevelManager = audioLevelManager;
        }
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        execute(jobExecutionContext.getFireTime());
    }
    private void execute(Date date) {
        //这里要解决的问题是定时器和checkAndStart同时执行，我已经遇到好多次因为Loading图标关不掉的问题，原因就是
        //timeShower被提前初始化了（启动过程中，Spring还没完全就绪，定时器先把timeShower给初始化了）
        if (timeShower == null) {
            return;
        }
        LocalDateTime now = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        refreshLedDndMode(now);
        if (now.getMinute() % 30 == 0) {
            SingleThreadCreator.getVacationRefresher().execute(clockInfoService::refreshChinaVacation);
        }
        if (componentManager != null && audioLevelManager != null) {
            checkAndDoAClockNotify(now);
        } else {
            return;
        }
        RunnableClockInfo nextRunnableClock = clockInfoService.getNextClockInfo(true);
        if (nextRunnableClock != null && nextRunnableClock.getClockInfos() != null) {
            List<ClockInfoDTO> nextClockInfo = nextRunnableClock.getClockInfos();
            List<MultiFilePlayer.RepeatTimesConfig> configs = new ArrayList<>(nextClockInfo.size());
            for (ClockInfoDTO c : nextClockInfo) {
                if (c.getAudios() != null && c.getAudios().size() > 0) {
                    for (AudioInfoDTO a : c.getAudios()) {
                        ClockRepeatTimesConfig config = new ClockRepeatTimesConfig();
                        config.setDes(c.getClkName());
                        config.setFile(new File(a.getAudioPath()));
                        config.setRepeatTimes(a.getRepeatTimes());
                        config.setClockId(c.getId());
                        config.setVolume(c.getClkVolume());
                        configs.add(config);
                    }
                }
            }
            if (configs.size() > 0) {
                try {
                    AudioPlayer audioPlayer = new MultiFilePlayer(configs, componentManager :: showAlarm);
                    audioPlayer.setPlayBeforeVolume(playBeforeVolume);
                    audioLevelManager.playMixer(audioPlayer, 2);
                } catch (Exception e) {
                }
            }
        }
    }

    /**
     * 启动完毕，打开屏幕显示器
     */
    public void checkAndStartShow() {
        if (timeShower == null) {
            log.info("启动完毕，开始打开屏幕");
            LoadingComponent.close();
            log.info("关闭loading");
            refreshLedDnd();
            refreshClockDnd();
            log.info("屏幕已打开");
        }
    }

    /**
     * 当变更了ledDndMode模式时，需要刷新这里
     */
    public void refreshLedDnd() {
        ledDndMode = ledDndModeSettingService.getSettingByType(DndModeTypeEnum.LED.getType());
        refreshLedDndMode(LocalDateTime.now());
    }

    /**
     * 当变更了clockDndMode模式时，需要刷新这里
     */
    public void refreshClockDnd() {
        clockDndMode = ledDndModeSettingService.getSettingByType(DndModeTypeEnum.CLOCK_NOTIFY.getType());
    }

    private void refreshLedDndMode(LocalDateTime now) {
        EmptyPlayer emptyPlayer = null;
        if (ledDndModeSettingService.checkIsInDnd(ledDndMode, now)) {
            if (lastLedDndModeStatus == 1) {
                return;
            }
            lastLedDndModeStatus = 1;
            try {
                emptyPlayer = new EmptyPlayer((AudioNoEffectComponentsSupplier)() -> new SimpleClockCompatibilityComponent(getSimpleClockComponents()));
            } catch (Exception e) {
                log.error("创建空播放器失败[精简模式钟表]:",e);
            }
        } else {
            if (lastLedDndModeStatus == 2) {
                return;
            }
            lastLedDndModeStatus = 2;
            try {
                emptyPlayer = new EmptyPlayer((AudioNoEffectComponentsSupplier)() -> new ClockCompatibilityComponent(getNormalClockComponents()));
            } catch (Exception e) {
                log.error("创建空播放器失败[普通模式钟表]:",e);
            }
        }
        if (emptyPlayer != null) {
            emptyPlayer.setPlayBeforeVolume(playBeforeVolume);
            audioLevelManager.playMixer(emptyPlayer, 4);
            this.timeShower = emptyPlayer;
        }
    }

    private ComponentManager.ClockComponents getNormalClockComponents() {
        return componentManager.showClock(()->{
            try {
                WeatherInfoDTO weatherInfo = weatherService.getWeatherInfo();
                WeatherComponent.WeatherInfo w = new WeatherComponent.WeatherInfo();
                w.setMin(Integer.parseInt(weatherInfo.getMin()));
                w.setMax(Integer.parseInt(weatherInfo.getMax()));
                return w;
            } catch (Exception e) {
                log.error("获取天气信息失败:",e);
            }
            return null;
        });
    }

    private ComponentManager.SimpleClockComponents getSimpleClockComponents() {
        return componentManager.showClockSimple();
    }

    /**
     * 检查并开始整点报时
     * @param now
     */
    private void checkAndDoAClockNotify(LocalDateTime now) {
        if (now.getMinute() == 0 && now.getSecond() == 0) {
            if (!ledDndModeSettingService.checkIsInDnd(clockDndMode, now)) {
                int hour = now.getHour();
                try {
                    String fileName = String.valueOf(hour);
                    while (fileName.length() < 3) {
                        fileName = "0" + fileName;
                    }
                    fileName = fileName + ".wav";
                    WavPlayer wavPlayer = new WavPlayer(new File("music/clock/" + fileName), (AudioNoEffectComponentsSupplier)() -> new AClockCompatibilityComponent(componentManager.showAClockNotify()));
                    wavPlayer.setPlayBeforeVolume(playBeforeVolume);
                    try {
                        wavPlayer.setVolume(volumeSettingService.getVolume());
                    } catch (Exception e) {
                        log.error("为整点报时调整音量失败:",e);
                    }
                    audioLevelManager.playMixer(wavPlayer, 1);
                } catch (Exception e) {
                }
            }
        }
    }

    /**
     * 让整点报时组件也兼容musicEffect组件
     */
    @Getter
    public static class AClockCompatibilityComponent extends ComponentManager.MusicEffectBaseComponent {
        private ComponentManager.AClockComponents aClockComponents;

        public AClockCompatibilityComponent(ComponentManager.AClockComponents aClockComponents) {
            super(null, null, null);
            this.aClockComponents = aClockComponents;
        }
    }

    /**
     * 让整点报时组件也兼容musicEffect组件
     */
    @Getter
    public static class ClockCompatibilityComponent extends ComponentManager.MusicEffectBaseComponent {
        private ComponentManager.ClockComponents clockComponents;

        public ClockCompatibilityComponent(ComponentManager.ClockComponents clockComponents) {
            super(null, null, null);
            this.clockComponents = clockComponents;
        }
    }

    /**
     * 让整点报时组件也兼容musicEffect组件
     */
    @Getter
    public static class SimpleClockCompatibilityComponent extends ComponentManager.MusicEffectBaseComponent {
        private ComponentManager.SimpleClockComponents simpleClockComponents;

        public SimpleClockCompatibilityComponent(ComponentManager.SimpleClockComponents clockComponents) {
            super(null, null, null);
            this.simpleClockComponents = simpleClockComponents;
        }
    }

    public interface AudioNoEffectComponentsSupplier<T> extends AudioEffectComponentsSupplier<T>{
        @Override
        default boolean open() {
            return false;
        }
    }

    @Data
    public static class ClockRepeatTimesConfig extends MultiFilePlayer.RepeatTimesConfig {
        private Long clockId;
    }
}
